<?php
class Macys_Oauth_Oauth {

	private $log;

	//CONST OAUTH_SESSION = 'Macys_OAuth';
	//CONST OAUTH_SECURE_SESSION = 'Macys_Secure_Access';

	/**
	 * Uses the OAuth 2.0 Spec
	 * This as experimental implementation of WSSG Oauth 2.0 spec
	 */
	CONST CLIENTID = 'client_id';
	CONST CLIENTSECRET = 'client_secret';
	CONST REDIRECT_URL = 'redirect_uri';
	CONST RESPONSE_TYPE = 'response_type';
	CONST SCOPE = 'scope';
	CONST STATE = 'state';

	/**
	 * Convert Code to Access Token
	 */
	CONST GRANTTYPE = 'grant_type';

	/**
	 * Array of persistent variables stored.
	 */
	protected $conf = array();

	/**
	 * Array of Variable names that can be passed via constructor
	 * Excluding endpoints
	 */
	private $allowedVars = array('client_id', 'client_secret', 'redirect_uri',
								'response_type', 'scope','state',
								'code', 'username', 'password','logout_redirect_url');

	/**
	 *
	 */
	private $oauthServerUris = array('authorize_uri', 'mobile_flow_uri','logout_uri');
	//private $apiServerUris = array('code_access_token_uri');

	/**
	 * Session FOr Macys
	 */
	private $sessionInfo;


	// Stuff that should get overridden by subclasses.
	//
	// I don't want to make these abstract, because then subclasses would have
	// to implement all of them, which is too much work.
	//
	// So they're just stubs. Override the ones you need.

	/**
	 * Initialize a Mashery / Macys WSSG OAuth2.0 Application.
	 *
	 * @param $config
	 *   An associative array as below:
	 *   - base_uri: The base URI for the OAuth2.0 endpoints.
	 *   - code: (optional) The authorization code.
	 *   - username: (optional) The username.
	 *   - password: (optional) The password.
	 *   - client_id: (optional) The application ID.
	 *   - client_secret: (optional) The application secret.
	 *   - authorize_uri: (optional) The end-user authorization endpoint URI.
	 *   - access_token_uri: (optional) The token endpoint URI.
	 *   - services_uri: (optional) The services endpoint URI.
	 */
	public function __construct($config = array()) {

		$this->log = Zend_Registry::get('log');
		$this->sessionInfo = new Macys_Base_Session();

		// Default Values
		$this->sessionData('scope', 'testing');
		$this->sessionData('state', 'LibraryTestingInProgress');

		//Set the WSSG Default values if passed
		// We must set base_uri first.
		// will also unset 'base_uri' and 'base_common' if they are set
		$config = $this->setBaseUri($config);

		// get the values from Config file
		$iniValues = $this->getConfigValues('oauth');

		// if we miss to get the BaseURI from $config then load form INI file
		$this->setBaseUri($iniValues);


		// Use predefined OAuth2.0 params, or get it from input.
		foreach ($this->allowedVars as $name) {
			if (isset($config[$name])) {
				$this->sessionData($name, $config[$name]);
				unset($config[$name]);
			}
			else {
				if (isset($iniValues[$name])) {
					$this->sessionData($name, $iniValues[$name]);
				}
			}
		}
		foreach ($this->oauthServerUris as $name) {
			if (isset($config[$name])) {
				if (substr($config[$name], 0, 4) == "http")
				$this->sessionData($name, $config[$name]);
				else
				$this->sessionData($name, $this->sessionData('base_uri') . $config[$name]);
				unset($config[$name]);
			}
			elseif (isset($iniValues[$name])) {
				if (substr($iniValues[$name], 0, 4) == "http")
				$this->sessionData($name, $iniValues[$name]);
				else
				{
					$url = '';
					$url = $this->sessionData('base_uri') . $iniValues[$name];
					$this->sessionData($name, $url);
				}

			}
		}
		// Other else configurations.
		foreach ($config as $name => $value) {
			$this->sessionData($name, $value);
		}
		$this->setMasheryUri();
	}


	public function setResponseType($responseType) {

		$this->sessionData(self::RESPONSE_TYPE,$responseType);
		$this->log->debug('Response Type Set to : ' . $this->getResponseType());

		return $this;
	}

	public function getResponseType() {
		return $this->sessionData(self::RESPONSE_TYPE);
	}

	/**
	 * Load BaseURI only once If repeately ask for Load do not load
	 * @param Array $confArray
	 */
	private function setBaseUri($confArray = array()) {
		if (is_null($this->sessionData('base_uri'))){
			if (isset($confArray['base_uri'])) {
				if (isset($confArray['base_common'])) {
					$this->sessionData('base_uri', $confArray['base_uri'] . $confArray['base_common']);
					unset($confArray['base_common']);
				}
				else {
					$this->sessionData('base_uri', $confArray['base_uri']);
				}
				unset($confArray['base_uri']);
			}
		}
		return $confArray;
	}


	private function getConfigValues($key = 'oauth') {
		$registry =new Zend_Registry();
		$iniVals = $registry->get('Macys');
		$iniValues = $iniVals[$key];
		return $iniValues;
	}

	private function setMasheryUri() {
		$apiValues = $this->getConfigValues('api');
		if(isset($apiValues['api_url'])){
			$this->sessionData('api_uri', $apiValues['api_url']);
		}
		else {
			$this->log->crit('API_URI not specified');
			//throw new Macys_Oauth_Exception('Mashery Server Host not Set, Please Update Config', '8000');
		}
		if(isset($apiValues['code_access_token_uri'])) {

			$this->sessionData('code_access_token_uri', $this->sessionData('api_uri') . $apiValues['code_access_token_uri']);
		}
		else {
			$this->sessionData('code_access_token_uri', $apiValues['code_access_token_uri']);

		}
	}




	/**
	 * Redirect the User to OAuth URL will be waiting for getting the code / access_token back
	 */
	public function doAuthorize() {
		$url = $this->getAuthorizeUrl();
		$this->log->debug('Redirecting to OAuth URL : ' . $url);
		header("Location: " . $url);
		exit;
	}

	/**
	 * Prepare the URL to display the OAuth Screen of Macys Gateway
	 */
	protected function getAuthorizeUrl() {
		$params = array (
		self::CLIENTID => $this->sessionData('client_id'),
		self::REDIRECT_URL => $this->sessionData('redirect_uri') ,
		self::RESPONSE_TYPE => $this->sessionData('response_type') , // setup to CODE for now
		self::SCOPE => $this->sessionData('scope'),
		self::STATE => $this->sessionData('state')
		);
		return $this->sessionData('authorize_uri') . '?' . http_build_query($params);
	}

	public function doLogout() {
		//Logout the User from This APP
		$this->sessionUnset(Macys_Constants::SECURE_DATA);

		$params = array (
		
		self::CLIENTID => $this->sessionData('client_id'),
		self::REDIRECT_URL => $this->sessionData('logout_redirect_url') ,
		// JIRA : MCOMPS-2178
		//'clientId' => $this->sessionData('client_id'),
		//'redirectUrl' => $this->sessionData('logout_redirect_url') ,
		//self::RESPONSE_TYPE => $this->sessionData('response_type') ,
		self::SCOPE => $this->sessionData('scope'),
		self::STATE => $this->sessionData('state')
		);
		$url = $this->sessionData('logout_uri') . '?' . http_build_query($params);
		$this->log->debug('Redirecting to Logout URL : ' . $url);
		header("Location: " . $url);
		exit;
	}

	public function callback($response = array()) {
		if (isset($response)) {
			$this->log->debug('Callback Response : ' . implode(',', $response));
			if ($this->sessionData('response_type') == 'code') {
				if(isset($response['code'])) {
				$arrayResp = $this->codetoAccessToken($response['code']);
				return $this->setTokenInSession($arrayResp);
				}
				else {
					$this->log->crit("OAuth Response from Code Flow has Errored : ");
					return "Return Response did not have Response Code";
					
				}
			}
			if ($this->sessionData('response_type') == 'token') {
				return $this->setTokenInSession($response);
			}
			return "Application Forgot Response-type set while sending user to OAuth Screen";
		}
		else {
			return "Response Type not Set";
		}
	}

	/**
	 * Mobile Authorize
	 * Enter description here ...
	 * @param string $username
	 * @param string $password
	 */
	public function mobileAuthorize($username, $password) {
		try {
			$response = $this->mobileAuthorizeClient($username, $password);
			return $this->setTokenInSession($response);
		}
		catch (Exception $e) {
			$this->log->crit('Mobile Authorize exception : ' . $e->getMessage());

		}
		return "Exception Occured in Mobile Authorize";

	}


	/**
	 * Authenticates the User as per Mobile Flow Pattern
	 * @param string $username
	 * @param string $password
	 */
	public function mobileAuthorizeClient($username = null, $password = null) {
		//http://localhost:8080/WebsiteServicesGateway/v2/oauth2/token?
		//grantType=password&username=testwssg33@macys.com&password=testme
		if (isset($username) && isset($password)) {
			$url = $this->sessionData('mobile_flow_uri');
			$client = new Macys_Base_HttpClient();
			$client->setUri($url);
			$client->setCurlSupport();
			$client->setAuth($this->sessionData('client_id'), $this->sessionData('client_secret'));
			$client->setParameterPost('username' , $username);
			$client->setParameterPost('password', $password);
			$client->setParameterPost(self::GRANTTYPE, 'password');
			$client->setContentType();
			$client->setAcceptType();
			$client->request('POST');
			return $client->getResponseBodyArray();
		}
	}

	private function setTokenInSession($token = array()) {
		$flag = false;
		$this->sessionUnset(Macys_Constants::SECURE_DATA);

		if (isset($token["access_token"])) {
			$this->secureSessionData('access_token' , $token["access_token"]);
			$flag = true;
		}
		if (isset($token["refresh_token"]))
		$this->secureSessionData('refresh_token' , $token["refresh_token"]);

		if (isset($token["expires_in"]))
		$this->secureSessionData('expires_in' , $token["expires_in"]);

		if (isset($token["token_type"]))
		$this->secureSessionData('token_type' , $token["token_type"]);

		if (isset($token["scope"]))
		$this->secureSessionData('scope' , $token["scope"]);

		if ($flag == FALSE) {
			$s = " ";
			if(is_array($token)) {
			foreach ($token as $k => $v)
			$s .= $k  . " = " . $v . ",  ";
			return $s;
			}
			else {
				return $token;
			}
		}
		return $flag;
	}



	/**
	 * Using php Curl Adapter we will get the access token
	 * Returns a Array
	 * @param array $code
	 */
	public function codetoAccessToken($code = null) {
		if ($code != null) {
			try {
				$url = $this->sessionData('code_access_token_uri');
				$client = new Macys_Base_HttpClient();
				//http://macys-registry.api.mashery.com/token
				//?grant_type=authorization_code&
				//code=q9b8bndqxtc9u85ay9qscent&
				//redirect_uri=http%3A%2F%localhost
				$client->setUri($url);
				$client->setCurlSupport();
				$client->setAuth($this->sessionData('client_id'), $this->sessionData('client_secret'));
				//$client->setEncType('application/x-www-form-urlencoded');
				$client->setParameterPost('code', $code);
				$client->setParameterPost(self::GRANTTYPE, 'authorization_code');
				$client->setParameterPost('redirect_uri', $this->sessionData('redirect_uri'));
				$client->setContentType();
				$client->setAcceptType();
				$client->request('POST');
				//$this->log->debug($client->getLastResponse()->getBody());
				return $client->getResponseBodyArray();
			}
			catch (Exception $e) {
				$this->log->warn('Error Occured in codetoAccessToken : ' . $e->getMessage());
			}
		}
	}


	public function getRefreshToken() {
		$newToken = $this->refreshToken();
		if (!is_null($newToken)) {
			$this->setTokenInSession($newToken);
			return $this;
		}
		else {
			$this->log->crit('Unable to get Refresh Token New Token is not avaialble ');
			//throw new Macys_Oauth_Exception('Unable to refresh Token', '200');
		}
	}

	private function refreshToken() {
		try {
			$val =  $this->secureSessionData('refresh_token');
			if (!is_null($val)) {
				//$this->setMasheryUri();
				$client = new Macys_Base_HttpClient();
				$client->setCurlSupport();
				$client->setMasheryAuth($this->sessionData('client_id'), $this->sessionData('client_secret'));
				$client->setUri($this->sessionData('code_access_token_uri'));
				$client->setAuth($this->sessionData('client_id'), $this->sessionData('client_secret'));
				$client->setContentType();
				$client->setAcceptType();
				$client->setParameterPost(self::GRANTTYPE, 'refresh_token');
				$client->setParameterPost('refresh_token', $val);
				$client->request('POST');
				return $client->getResponseBodyArray();
			}
		}
		catch (Exception $e) {
			$this->log->crit('Refresh Token Exception : '. $e->getMessage());
		}
		return null;
	}


	/**
	 * Wrapper for Session Data Management of this Class
	 * Stores / Retrives the Session Data
	 * @param string $key
	 * @param Mixed $data
	 */
	private function sessionData($key, $data=null) {
		return $this->sessionInfo->sessionData(Macys_Constants::SESSION, $key, $data);
	}

	private function secureSessionData($key, $data=null) {
		return $this->sessionInfo->sessionData(Macys_Constants::SECURE_DATA, $key, $data);
	}

	private function sessionUnset($namespace) {
		$this->sessionInfo->sessionUnset($namespace);
		return $this;
	}


}